﻿//
// XN4SL - Xna Framework for Silverlight.
//
using Xn4Sl.Framework;
using System;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.IO;
using System.Windows.Media;
using System.Windows;
using System.Xml;
using System.Windows.Documents;

namespace Xn4Sl.Framework.Graphics
{
    /// <summary>
    /// Represents a font texture.
    /// </summary>
    public sealed class SpriteFont
    {
        /// <summary>
        /// Gets a collection of all the characters that are included in the font.
        /// </summary>
        public ReadOnlyCollection<char> Characters
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Gets or sets the default character for the font.
        /// </summary>
        public char? DefaultCharacter { get; set; }

        /// <summary>
        /// The line spacing, in pixels, of this font.
        /// </summary>
        public int LineSpacing { get; set; }

        /// <summary>
        /// Gets or sets the spacing of the font characters.
        /// </summary>
        public float Spacing { get; set; }

        /// <summary>
        /// Font Family.
        /// </summary>
        private FontFamily fontFamily = new FontFamily("Verdana");
        internal FontFamily FontFamily
        {
            get
            {
                return this.fontFamily;
            }
            set
            {
                this.fontFamily = value;
            }
        }

        /// <summary>
        /// Font Style.
        /// </summary>
        private FontWeight fontStyle = FontWeights.Normal;
        internal FontWeight FontStyle
        {
            get
            {
                return this.fontStyle;
            }
            set
            {
                this.fontStyle = value;
            }
        }

        /// <summary>
        /// Font file source.
        /// </summary>
        private FontSource fontSource;
        internal FontSource FontSource
        {
            get
            {
                return this.fontSource;
            }
            set
            {
                this.fontSource = value;
            }
        }

        /// <summary>
        /// Font Size.
        /// </summary>
        private double fontSize = 12;
        internal double FontSize 
        {
            get
            {
                return this.fontSize;
            }
            set
            {
                this.fontSize = value;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="stream">Font file source.</param>
        /// <param name="fontFamily">Font family name.</param>
        /// <param name="fontStyle">Font weight.</param>
        /// <param name="fontSize">Font size.</param>
        private SpriteFont(Stream fontStream, string fontFamily, FontWeight fontStyle, double fontSize)
        {
            //
            // Set font properties.
            //
            this.fontFamily = new FontFamily(fontFamily);
            this.fontStyle = fontStyle;
            this.fontSize = fontSize;
            this.FontSource = new FontSource(fontStream);
        }

        /// <summary>
        /// Returns the height and width of a given string as a Framework.Vector2.
        /// </summary>
        /// <param name="text">The string to measure.</param>
        /// <returns>The height and width, in pixels, of text, when it is rendered.</returns>
        public Vector2 MeasureString(string text)
        {
            return new Vector2((float)this.FontSize); 
        }

        /// <summary>
        /// Returns the height and width of a mutable string as a Framework.Vector2.
        /// </summary>
        /// <param name="text">The mutable (read/write) string to measure.</param>
        /// <returns>The height and width, in pixels, of text, when it is rendered.</returns>
        public Vector2 MeasureString(StringBuilder text)
        {
            return new Vector2((float)this.FontSize);
        }

        /// <summary>
        /// Creates a sprite font resource from a file stream.
        /// </summary>
        /// <param name="graphicsDevice"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        internal static SpriteFont FromFile(Stream stream)
        {
            //
            // Default font properties;
            //
            string fontFamily = "Verdana";
            double fontSize = 12;
            FontWeight fontStyle = FontWeights.Normal;

            if (stream != null)
            {
                //
                // Read the font settings.
                //
                try
                {
                    using (XmlReader reader = XmlReader.Create(new StreamReader(stream)))
                    {
                        while (reader.Read())
                        {
                            if (reader.IsStartElement() && reader.LocalName == "XnaContent")
                            {
                                using (XmlReader reader2 = reader.ReadSubtree())
                                {
                                    while (reader2.Read())
                                    {
                                        if (reader2.IsStartElement() && reader2.LocalName == "Asset")
                                        {
                                            if (reader.GetAttribute("Type") != "Graphics:FontDescription") continue;

                                            while (reader2.Read())
                                            {
                                                if (reader2.NodeType == XmlNodeType.Element)
                                                {
                                                    if (reader2.LocalName == "FontName")
                                                    {
                                                        fontFamily = reader2.ReadElementContentAsString();
                                                    }
                                                    else if (reader2.LocalName == "Size")
                                                    {
                                                        fontSize = reader2.ReadElementContentAsDouble();
                                                    }
                                                    else if (reader2.LocalName == "Style")
                                                    {
                                                        string value = reader2.ReadElementContentAsString();

                                                        if (value[0] == 'B')
                                                        {
                                                            fontStyle = FontWeights.Bold;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    reader2.Close();
                                }
                            }
                        }
                        reader.Close();
                    }
                }
                catch { }
                finally
                {
                    stream.Close();
                }
            }

            stream = null;

            if(fontFamily.Contains("#"))
            {
                int sep = fontFamily.IndexOf('#');
                string fontFile = fontFamily.Substring(0, sep);
                fontFamily = fontFamily.Substring(sep + 1, fontFamily.Length - sep - 1);
                //
                // TODO: Use ContentManager.OpenStream here.
                //
                try
                {
                    XmlXapResolver xmlRes = new XmlXapResolver();
                    stream = xmlRes.GetEntity(new Uri(fontFile, UriKind.Relative), null, null) as Stream;
                }
                catch
                {
                    throw new Exception(String.Format("Failed to load: {0}!", fontFile));
                }
            }
            return new SpriteFont(stream, fontFamily, fontStyle, fontSize);
        }
    }
}
